#undef  xuser
#define xuser mixc::io_dir::inc
#include"configure/limit.hpp"
#include"configure/switch.hpp"
#include"io/dir.hpp"
#include"io/private/path_buffer.hpp"
#include"lang/cxx/is_contains.hpp"
#include"lang/cxx/strcat.hpp"
#include"lang/cxx.hpp"
#include"macro/xdefer.hpp"
#include"macro/xmodify.hpp"
#include"macro/xwarning.hpp"
#include"memop/copy.hpp"
#include"utils/memory.hpp"

#if xis_windows
#include<Windows.h>
#if xis_msvc_native
#pragma comment(lib, "Shell32.lib")
#endif
#elif xis_linux
#include<dirent.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
#include"cmd.hpp"
#endif

xwarning_disable(26444)

#define xlinux_cmd_rm       "/bin/rm", "rm", "-rf"
#define xlinux_cmd_mv       "/bin/mv", "mv", "-r"
#define xlinux_cmd_cp       "/bin/cp", "cp", "-r"
#define xlinux_cmd_mkdir    "/bin/mkdir", "mkdir", "-p"

namespace mixc::io_dir::inc{
    extern bool is_exist(inc::c08 const & path){
        using buff          = inc::path_buffer;
        buff buf;
        auto source         = buf.alloc(path);

    #if xis_windows
        auto attr           = GetFileAttributesA((asciis)source);
        auto exist          = INVALID_FILE_ATTRIBUTES != attr and 0 != (attr & FILE_ATTRIBUTE_DIRECTORY);
    #elif xis_linux
        typedef struct stat meta_t;
        auto meta           = meta_t{};
        auto exist          = stat(asciis(source), & meta) != -1 and (meta.st_mode & S_IFDIR) != 0;
    #else
        #error "os miss match"
    #endif

        buf.free(source, path);
        return exist;
    }
}

namespace mixc::io_dir::origin{
    bstate_t dir::remove() const{
        using buff = inc::path_buffer;
        buff buf;
        auto source = buf.alloc(xthe.m_path);
        auto is_success = 

    #if xis_windows
        ::RemoveDirectoryA(asciis(source)) ? bstate_t::success : bstate_t::fail;
    #elif xis_linux
        cmd(xlinux_cmd_rm, source);
    #else
        #error "os miss match"
    #endif

        buf.free(source, m_path);
        return is_success;
    }

    bstate_t dir::move_to(inc::c08 new_path) const{
        using buff          = inc::path_buffer;
        buff buf;
        auto source         = buf.alloc(xthe.m_path);
        auto target         = buf.alloc(new_path);

    #if xis_windows
        auto && opr         = SHFILEOPSTRUCTA{};
        opr.wFunc           = FO_MOVE;
        opr.pFrom           = (asciis)source;
        opr.pTo             = (asciis)target;
        opr.fFlags          = FOF_SILENT | FOF_NOCONFIRMATION | FOF_NOCONFIRMMKDIR;
        auto is_success     = SHFileOperationA(& opr) == 0 ? bstate_t::success : bstate_t::fail;

    #elif xis_linux
        auto is_success     = cmd(xlinux_cmd_mv, source, target);
    #else
        #error "os miss match"
    #endif

        buf.free(target, new_path);
        buf.free(source, xthe.m_path);
        return is_success;
    }

    bstate_t dir::copy_to(inc::c08 new_path) const{
        using buff          = inc::path_buffer;
        buff buf;
        auto source         = buf.alloc(xthe.m_path);
        auto target         = buf.alloc(new_path);

    #if xis_windows
        auto && opr         = SHFILEOPSTRUCTA{};
        opr.wFunc           = FO_COPY;
        opr.pFrom           = (asciis)source;
        opr.pTo             = (asciis)target;
        opr.fFlags          = FOF_SILENT | FOF_NOCONFIRMATION | FOF_NOCONFIRMMKDIR;
        auto is_success     = SHFileOperationA(& opr) == 0 ? bstate_t::success : bstate_t::fail;
    #elif xis_linux
        auto is_success     = cmd(xlinux_cmd_cp, source, target);
    #else
        #error "os miss match"
    #endif

        buf.free(target, new_path);
        buf.free(source, xthe.m_path);
        return is_success;
    }

    void dir::foreach_template(bool for_file, inc::c08 path, inc::icallback< loop_t(asciis path) > invoke){
        using buff = inc::path_buffer;
        buff buf;
        auto go_on          = true;
        auto is_dir         = false;

    #if xis_windows
        auto entry          = WIN32_FIND_DATAA{};
        auto temp_path      = path;
        auto offset         = path->length();
        temp_path->length   = offset + 3;

        auto source         = buf.alloc_forcedly(temp_path); // 强制分配
        source[offset + 0]  = '\\';
        source[offset + 1]  = '*';
        source[offset + 2]  = '\0';

        xdefer{
            buf.free(source, temp_path);
        };

        auto fd             = FindFirstFileA((asciis)source, xmodify(entry));

        if (fd == INVALID_HANDLE_VALUE){
            return;
        }

        xdefer{
            ::CloseHandle(fd);
        };

        #define xentry_name     (entry.cFileName)
        #define xis_dir         (0 != (entry.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
        #define xhas_next       (FindNextFileA(fd, xmodify(entry)) != 0)

    #elif xis_linux
        using buff = inc::path_buffer;
        struct
        dirent * entry;
        DIR * fd;

        auto source         = buf.alloc(path);

        xdefer{
            buf.free(source, path);
        };

        if (fd = opendir(asciis(source)); fd == nullptr){
            return;
        }
        else{
            entry           = readdir(fd);
        }

        xdefer{
            ::closedir(fd);
        };

        #define xentry_name     (entry->d_name)
        #define xis_dir         ((entry->d_type & DT_DIR) != 0)
        #define xhas_next       ((entry = readdir(fd)) != nullptr)

    #endif

        for(; go_on; go_on = xhas_next){
            if (is_dir = xis_dir; for_file ^ is_dir){
                if (inc::c08{xentry_name} == "." or
                    inc::c08{xentry_name} == ".."){
                    continue;
                }
                if (invoke(xentry_name) == loop_t::finish){
                    break;
                }
            }
        }

        #undef  xentry_name
        #undef  xis_dir
        #undef  xhas_next
    }

    bstate_t dir::create() const {
        // 防止注入攻击
        if (inc::c08{m_path}.is_contains('\"')){
            return bstate_t::fail;
        }

    #if xis_windows
        char buf[inc::limit::io_private_dir_create];
        auto mkdir          = inc::c08{"mkdir \""}.strcat({m_path, "\""}, [&](uxx length){
            auto ptr        = buf;

            if (length >= sizeof(buf) - 1){
                ptr         = inc::memory::alloc<char>(inc::memory::size{length + 1});
            }

            ptr[length]     = '\0';
            return ptr;
        });

        auto is_success     = system((asciis)mkdir) == 0 ? bstate_t::success : bstate_t::fail;

        if (mkdir.operator char *() != buf){
            inc::memory::free<char>((char *)mkdir, inc::memory::size{mkdir->length + 1});
        }

    #elif xis_linux
        using buff          = inc::path_buffer;
        buff buf;
        auto target         = buf.alloc(xthe.m_path);
        auto is_success     = cmd(xlinux_cmd_mkdir, target);
        buf.free(target, m_path);
    #else
        #error "os miss match"
    #endif
        return is_success;
    }

    bstate_t dir::as_cwd() const {
        using buff          = inc::path_buffer;
        buff buf;
        auto target         = buf.alloc(xthe.m_path);
        auto is_success     = 

    #if xis_windows
        ::SetCurrentDirectoryA((asciis)target) ? bstate_t::success : bstate_t::fail;

    #elif xis_linux
        ::chdir(asciis(target)) == 0 ? bstate_t::success : bstate_t::fail;
    #endif

        buf.free(target, m_path);
        return is_success;
    }
}

#undef  xlinux_cmd_rm
#undef  xlinux_cmd_mv
#undef  xlinux_cmd_cp
#undef  xlinux_cmd_mkdir